package com.atguigu.tingshu.common.login;

import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * @Author zlq
 * @CreateTime 2025/2/22
 * 登录认证切面类
 */
@Aspect
@Component
@Slf4j
public class TsLoginAspect {
    @Resource
    private RedisTemplate redisTemplate;

    //  利用反射机制+aop的环绕通知，实现是否需要登录功能！
    @Around("execution(* com.atguigu.tingshu.*.api.*.*(..)) && @annotation(tsLogin)")
    public Object doTsLoginAspectAround(ProceedingJoinPoint joinPoint,TsLogin tsLogin) throws Throwable {
        Object object = new Object();
        //  系统是如何处理登录功能！
        /*
            1.  调用微信小程序登录 api 功能！
            2.  登录成功：（需要将用户信息存储到redis, 返回一个token，让前端写入 header） 登录失败：
            login(){
                if(success){
                    key = userId; value = userInfo.toJsonString();
                    redis set key value;
                    return token;
                }
            }
         */

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String token = request.getHeader("token");

        //1、判断是否有@TsLogin+requireLogin为true，
        if (tsLogin.requireLogin()) {
            //如果requireLogin为true，进行登录认证

            //2、判断请求中是否有token

            if (StringUtils.isEmpty(token)) {
                //无，提示前端，跳转至登录
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
            //3、判断redis是否有token对应UserInfo，
            UserInfo userInfo =(UserInfo) redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);
            if (null == userInfo) {
                //- 无，提示前端，跳转至登录
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }

        }
        try {
            //4、如果有token，redis有对应UserInfo，将UserId绑定在线程上
            if(!StringUtils.isEmpty(token)){
                UserInfo userInfo =(UserInfo) redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);
                if (null!=userInfo) {
                    //将UserId绑定在线程上，使用工具类common-utils >> AuthContextHolder
                    AuthContextHolder.setUserId(userInfo.getId());
                }
            }
            //5、放行方法
            object=joinPoint.proceed();
            return object;
        } finally {
            //6、环绕通知后，
            //  - 删除绑定在线程上的UserId，防止内存泄漏
            AuthContextHolder.removeUserId();
        }
    }

}
